package com.eksframework.commons.json;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 基于Jackson的json与对象之间转换的工具类
 */
public class EksJson {

    private static final String JSON_ARRAY_PREFIX = "[";
    private static final String JSON_ARRAY_SUFFIX = "]";
    private static ObjectMapper mapper = null;

    private EksJson() {
    }

    /**
     * 获取ObjectMapper
     *
     * @return ObjectMapper
     */
    @Nonnull
    public static ObjectMapper getMapper() {
        if (mapper != null) {
            return mapper;
        }
        synchronized (EksJson.class) {
            if (mapper != null) {
                return mapper;
            }
            mapper = new ObjectMapper();
        }
        return mapper;
    }

    /**
     * 创建ObjectMapper
     *
     * @return ObjectMapper
     */
    @Nonnull
    public static ObjectMapper createMapper() {
        return new ObjectMapper();
    }

    /**
     * 创建EksJackson
     *
     * @return EksJackson
     */
    @Nonnull
    public static EksJackson createEksJackson() {
        return new EksJackson();
    }

    /**
     * 复制当前静态的ObjectMapper
     *
     * @return EksJackson
     */
    @Nonnull
    public static EksJackson asEksJackson() {
        return new EksJackson(getMapper().copy());
    }

    /**
     * 复制ObjectMapper
     *
     * @return EksJackson
     */
    @Nonnull
    public static EksJackson asEksJackson(ObjectMapper mapper) {
        return new EksJackson(mapper.copy());
    }

    /**
     * 创建ObjectNode
     *
     * @return ObjectNode
     * @see ObjectMapper#createObjectNode()
     */
    @Nonnull
    public static ObjectNode createObjectNode() {
        return getMapper().createObjectNode();
    }

    /**
     * 创建ArrayNode
     *
     * @return ArrayNode
     * @see ObjectMapper#createArrayNode()
     */
    @Nonnull
    public static ArrayNode createArrayNode() {
        return getMapper().createArrayNode();
    }

    /**
     * JSON序列化对象，Object可以是POJO，也可以是Collection或数组
     *
     * @param object Object
     * @return String json string
     * @throws IllegalArgumentException if serialization failed
     * @see #toJson(ObjectMapper, Object)
     */
    @Nonnull
    public static String toJson(@Nullable Object object) {
        return toJson(getMapper(), object);
    }

    /**
     * JSON反序列化，把JSON字符串反序列化为JsonNode对象
     *
     * @param jsonString JSON字符串
     * @return JsonNode对象
     * @throws IllegalArgumentException if deserialization failed
     * @see #parseObject(ObjectMapper, String)
     */
    @Nonnull
    public static JsonNode parseObject(@Nullable String jsonString) {
        return parseObject(getMapper(), jsonString);
    }

    /**
     * JSON反序列化，把JSON字符串反序列化为对象
     *
     * @param jsonNode jsonNode数据
     * @param clazz    要反序列化的对象类
     * @param <T>      类
     * @return 反序列化对象
     * @throws NullPointerException     if the jsonNode or the clazz is null
     * @throws IllegalArgumentException if deserialization failed
     * @see #parseObject(ObjectMapper, JsonNode, Class)
     */
    @Nullable
    public static <T> T parseObject(@Nullable JsonNode jsonNode, @Nullable Class<T> clazz) {
        return parseObject(getMapper(), jsonNode, clazz);
    }

    /**
     * JSON反序列化，把JSON字符串反序列化为对象
     *
     * @param jsonString 要反序列化的Json字符串
     * @param clazz      要反序列化的对象类
     * @param <T>        类
     * @return 转换后的对象
     * @throws NullPointerException     if the jsonString or the clazz is null
     * @throws IllegalArgumentException if deserialization failed
     * @see #parseObject(String, Class, DateFormat)
     */
    @Nullable
    public static <T> T parseObject(@Nullable String jsonString, @Nullable Class<T> clazz) {
        return parseObject(jsonString, clazz, null);
    }

    /**
     * JSON反序列化，把JSON字符串反序列化为对象
     * 使用：typeReference = new TypeReference<InfoDataRequest<SessionDataQueryParam>>(){};
     *
     * @param jsonString    要反序列化的Json字符串
     * @param typeReference 要反序列化的对象类型
     * @param <T>           类
     * @return 转换后的对象
     * @throws NullPointerException     if the jsonString or the clazz is null
     * @throws IllegalArgumentException if deserialization failed
     * @see #parseObject(ObjectMapper, String, TypeReference)
     */
    @Nullable
    public static <T> T parseObject(@Nullable String jsonString, @Nullable TypeReference<T> typeReference) {
        return parseObject(getMapper().copy(), jsonString, typeReference);
    }

    /**
     * JSON反序列化，把JSON字符串反序列化为对象
     *
     * @param jsonString 要反序列化的Json字符串
     * @param clazz      要反序列化的对象类
     * @param df         日期格式
     * @param <T>        类
     * @return 转换后的对象
     * @throws IllegalArgumentException if deserialization failed
     * @see #parseObject(ObjectMapper, String, Class, DateFormat)
     */
    @Nullable
    public static <T> T parseObject(@Nullable String jsonString, @Nullable Class<T> clazz, @Nullable DateFormat df) {
        return parseObject(getMapper().copy(), jsonString, clazz, df);
    }

    /**
     * JSON反序列化，把JSON数组字符串反序列化为对象列表
     *
     * @param jsonArray 要反序列化的Json字符串
     * @param clazz     要反序列化的对象类
     * @param <T>       类
     * @return List
     * @throws IllegalArgumentException if deserialization failed
     * @see #parseArray(ObjectMapper, String, Class)
     */
    @Nonnull
    public static <T> List<T> parseArray(@Nullable String jsonArray, @Nullable Class<T> clazz) {
        return parseArray(getMapper(), jsonArray, clazz);
    }

    /**
     * 设置ObjectMapper
     *
     * @param objectMapper 初始化ObjectMapper
     */
    static void setMapper(ObjectMapper objectMapper) {
        mapper = objectMapper;
    }

    /**
     * JSON序列化对象，Object可以是POJO，也可以是Collection或数组
     *
     * @param mapper ObjectMapper
     * @param object Object
     * @return String json string
     * @throws IllegalArgumentException if serialization failed
     */
    static String toJson(ObjectMapper mapper, Object object) {
        if (object == null) {
            return "{}";
        }
        if (object instanceof String) {
            return (String) object;
        }
        try {
            return mapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            throw new IllegalArgumentException("deserialization failed", e);
        }
    }

    /**
     * JSON反序列化，把JSON字符串反序列化为JsonNode对象
     *
     * @param mapper     ObjectMapper
     * @param jsonString JSON字符串
     * @return JsonNode对象
     * @throws IllegalArgumentException deserialization failed
     */
    static JsonNode parseObject(ObjectMapper mapper, String jsonString) {
        if (jsonString == null) {
            return mapper.createObjectNode();
        }
        try {
            return mapper.readTree(jsonString);
        } catch (IOException e) {
            throw new IllegalArgumentException("deserialization failed", e);
        }
    }

    /**
     * JSON反序列化，把JSON字符串反序列化为对象
     * 使用：typeReference = new TypeReference<InfoDataRequest<SessionDataQueryParam>>(){};
     *
     * @param mapper        ObjectMapper
     * @param jsonString    要反序列化的字符串数据
     * @param typeReference 反序列化对象类型
     * @param <T>           类
     * @return 反序列化后的对象
     * @throws IllegalArgumentException if deserialization failed
     */
    static <T> T parseObject(ObjectMapper mapper, String jsonString, @Nullable TypeReference<T> typeReference) {
        if (Objects.isNull(jsonString) || Objects.isNull(typeReference)) {
            return null;
        }
        try {
            return mapper.readValue(jsonString, typeReference);
        } catch (IOException e) {
            throw new IllegalArgumentException("deserialization failed", e);
        }
    }

    /**
     * JSON反序列化，把JSON字符串反序列化为对象
     *
     * @param mapper   ObjectMapper
     * @param jsonNode 要反序列化的jsonNode数据
     * @param clazz    反序列化对象类
     * @param <T>      类
     * @return 反序列化后的对象
     * @throws IllegalArgumentException if deserialization failed
     */
    static <T> T parseObject(ObjectMapper mapper, JsonNode jsonNode, Class<T> clazz) {
        if (jsonNode == null || clazz == null) {
            return null;
        }
        try {
            String jsonString = mapper.writeValueAsString(jsonNode);
            return mapper.readValue(jsonString, clazz);
        } catch (IOException e) {
            throw new IllegalArgumentException("deserialization failed", e);
        }
    }

    /**
     * JSON反序列化，把JSON字符串反序列化为对象
     *
     * @param mapper     ObjectMapper
     * @param jsonString 要反序列化的Json字符串
     * @param clazz      要反序列化的对象类
     * @param df         日期格式
     * @param <T>        类
     * @return 转换后的对象
     * @throws IllegalArgumentException if deserialization failed
     */
    static <T> T parseObject(ObjectMapper mapper, String jsonString, Class<T> clazz, DateFormat df) {
        if (jsonString == null || "".equals(jsonString.trim())) {
            return null;
        }
        if (df != null) {
            mapper.setDateFormat(df);
        }
        try {
            return mapper.readValue(jsonString, clazz);
        } catch (Exception e) {
            throw new IllegalArgumentException("deserialization failed", e);
        }
    }

    /**
     * JSON反序列化，把JSON数组字符串反序列化为对象列表
     *
     * @param mapper    ObjectMapper
     * @param jsonArray 要反序列化的Json字符串
     * @param clazz     要反序列化的对象类
     * @param <T>       类
     * @return List
     * @throws IllegalArgumentException if deserialization failed
     */
    static <T> List<T> parseArray(ObjectMapper mapper, String jsonArray, Class<T> clazz) {
        if (jsonArray == null || clazz == null
                || !jsonArray.trim().startsWith(JSON_ARRAY_PREFIX)
                || !jsonArray.trim().endsWith(JSON_ARRAY_SUFFIX)) {
            return new ArrayList<>();
        }
        try {
            JavaType javaType = mapper.getTypeFactory().constructCollectionType(ArrayList.class, clazz);
            return mapper.readValue(jsonArray, javaType);
        } catch (Exception e) {
            throw new IllegalArgumentException("deserialization failed", e);
        }
    }
}
